'use client';

import type {
  IDifference,
  IPostEditInfo,
  IPostNewInfo,
  IUpdatePostEditInfoBody,
  IUpdatePostNewInfoBody,
} from '@/interfaces';
import type { TBody, TMetadata } from '@/types';
import {
  type FormEvent,
  type MouseEvent,
  useCallback,
  useEffect,
  useRef,
  useState,
} from 'react';
import diff from 'microdiff';
import { useSearchParams } from 'next/navigation';
import {
  EditPostContext,
  type TEditPostConfig,
  type TEditPostForm,
} from '@/contexts/edit';
import Editor from '@/app/[locale]/editor/wrapper';
import Menu from '@/app/[locale]/posts/[id]/edit/menu';
import Content from '@/app/[locale]/posts/[id]/edit/content';
import sanitizeHtml from 'sanitize-html';
import { useMutation } from '@tanstack/react-query';
import { updatePostEditInfo, updatePostNewInfo } from '@/services/api';
import {
  getDiffData,
  getFilterData,
  isEmpty,
  isNum,
  isValidLink,
} from '@/lib/tool';
import useToast from '@/hooks/useToast';
import Preview from '@/app/[locale]/posts/[id]/edit/preview';
import classNames from 'classnames';
import ContentLink from '@/app/[locale]/posts/[id]/edit/contentLink';
import { useTranslations } from 'use-intl';

export default function EditPostCore(
  this: any,
  {
    type,
    data,
    metadata,
  }: {
    type: 'newPost' | 'editPost';
    data: typeof type extends 'newPost' ? IPostNewInfo : IPostEditInfo;
    metadata: TMetadata;
  },
) {
  const isEdit = type === 'editPost';
  const id = isEdit ? data.basic.id : undefined;
  const initialData = isEdit ? data.content : '';
  const urlSearchParams = useSearchParams();
  const [tab, setTab] = useState(0);
  const [form, setForm] = useState<TEditPostForm>({
    allow: [],
    block: [],
    cover: '',
    customTags: [],
    name: '',
    otherStates: ['DEFAULT'],
    overview: '',
    contentType: 'DEFAULT',
    contentLink: '',
    secret: '',
    sectionId: '',
    statement: '',
    uploadCover: '',
    uploadCoverFile: '',
    uploadCoverObjectUrl: '',
  });
  const [differenceData, setDifferenceData] = useState<IDifference[]>([]);
  const [isSave, setIsSave] = useState(false);
  const { show } = useToast();
  const editorRef = useRef<{
    getEditorContent: () => string;
    setEditorContent: (value: any) => void;
  }>();
  const [isLoadPreview, setIsLoadPreview] = useState(false);
  const [previewContent, setPreviewContent] = useState('');
  const [isSaving, setIsSaving] = useState(false);
  const [config, setConfig] = useState<TEditPostConfig>({
    isCenterEditor: false,
  });
  const t = useTranslations('PostEditPage');

  const getEditPostForm = useCallback(() => {
    const { section, basic, details } = data;
    return {
      cover: basic.cover || '',
      customTags: basic.customTags,
      name: basic.name,
      otherStates:
        details.otherStates.length === 0 ? ['DEFAULT'] : details.otherStates,
      overview: basic.overview || '',
      contentType: basic.contentType,
      contentLink: basic.contentLink || '',
      sectionId: section.id + '',
      statement: basic.statement || '',
      allow: (details.allow || []).map((item) => item + ''),
      block: (details.block || []).map((item) => item + ''),
    };
  }, [data]);
  const getNewPostForm = useCallback(() => {
    const { sections } = data;
    return {
      sectionId:
        urlSearchParams.get('sectionId') ||
        urlSearchParams.get('sid') ||
        (sections.length > 0 ? sections[0].id + '' : ''),
    };
  }, [data, urlSearchParams]);

  useEffect(() => {
    let newForm: {};
    if (type === 'editPost') {
      newForm = getEditPostForm();
    } else {
      newForm = getNewPostForm();
    }

    setForm((prevState) => ({ ...prevState, ...newForm }));
  }, [getEditPostForm, getNewPostForm, type]);
  useEffect(() => {
    let obj: {};
    if (type === 'editPost') {
      obj = getEditPostForm();
    } else {
      obj = {};
    }

    setDifferenceData(diff(obj, form));
  }, [form, getEditPostForm, getNewPostForm, type]);

  const clientUpdatePostEditMutation = useMutation(
    async (variables: TBody<IUpdatePostEditInfoBody>) => {
      return await updatePostEditInfo(variables);
    },
  );
  const clientUpdatePostNewMutation = useMutation(
    async (variables: TBody<IUpdatePostNewInfoBody>) => {
      return (await updatePostNewInfo(variables)) as Response;
    },
  );

  async function onClickSave(
    e?: FormEvent<HTMLFormElement> | MouseEvent<HTMLAnchorElement>,
  ) {
    try {
      if (e) {
        e.preventDefault();
        e.stopPropagation();
      }

      setIsSave(true);
      checkForm();
      const body = getFilterData(getDiffData(differenceData));

      body.content = editorRef.current?.getEditorContent();
      body.overview = getOverview(body.overview);
      body.images = getImages(body.content);

      if (
        form.contentType === 'LINK' &&
        form.contentLink &&
        isValidLink(form.contentLink)
      ) {
        body.content = `<a href="${form.contentLink}" rel="noopener noreferrer" target="_blank">${form.contentLink}</a>`;
      } else if (body.content) {
        body.contentType = 'DEFAULT';
      } else {
        body.content = form.name;
        body.contentType = 'NONE';
      }

      const otherStates = form.otherStates;
      if (otherStates.includes('DEFAULT')) {
        body.otherStates = [];
        body.allow = [];
        body.block = [];
      }

      if (body.allow) {
        body.allow = body.allow
          .filter((value: string) => isNum(value))
          .map((value: string) => parseInt(value));
      }
      if (body.block) {
        body.block = body.block
          .filter((value: string) => isNum(value))
          .map((value: string) => parseInt(value));
      }

      if (isEmpty(body)) {
        show({
          type: 'SUCCESS',
          message: t('saveCompleted'),
        });
        return;
      }

      if (isEdit) {
        await clientUpdatePostEditMutation.mutateAsync({
          id: id,
          data: body,
        });

        show({
          type: 'SUCCESS',
          message: t('saveCompleted'),
        });

        setTimeout(() => {
          show({
            type: 'PRIMARY',
            message: t('returnToPostButton'),
          });
        }, 1200);
      } else {
        const { headers } = await clientUpdatePostNewMutation.mutateAsync({
          data: body,
        });

        show({
          type: 'SUCCESS',
          message: t('redirectAfterSave'),
        });

        await jumpToNewPost(headers.get('location'));
      }
    } catch (e) {
      if (isEdit) {
        clientUpdatePostEditMutation.reset();
      } else {
        clientUpdatePostNewMutation.reset();
      }

      show({
        type: 'DANGER',
        message: e,
      });
    } finally {
      setIsSave(false);
    }
  }

  async function onClickPreview() {
    try {
      setIsLoadPreview(true);
      setPreviewContent(editorRef.current?.getEditorContent || '');
    } catch (e) {
      show({
        type: 'DANGER',
        message: e,
      });
    } finally {
      setIsLoadPreview(false);
    }
  }

  async function jumpToNewPost(href: string | null) {
    if (isSaving) {
      await new Promise((resolve) => setTimeout(resolve, 5000));
    }

    setTimeout(() => {
      if (href) {
        location.href = href;
      } else {
        location.href = '/';
      }
    }, 1500);
  }

  function checkForm() {
    const { name, sectionId } = form;

    if (!name.trim()) {
      throw t('titleRequired');
    }

    if (!sectionId) {
      throw t('contentRequired');
    }
  }

  function getImages(content: string) {
    if (!content) {
      return [];
    }

    return (content.match(/<img.*?>/g) || [])
      .filter((item) => item.includes('width=') && item.includes('height='))
      .map((item) => {
        const exec = /src="([^"\s]+)"/g.exec(item);
        return exec ? exec[1].trim() : null;
      })
      .filter((item) => item)
      .filter((value, index, array) => array.indexOf(value) === index);
  }

  function getOverview(overview: string | null | undefined) {
    return overview ? sanitizeHtml(overview) : '';
  }

  return (
    <EditPostContext.Provider
      value={{
        tab,
        setTab,
        form,
        setForm,
        differenceData,
        isSave,
        setIsSave,
        onClickSave,
        isLoadPreview,
        setIsLoadPreview,
        onClickPreview,
        editorRef,
        metadata,
        isSaving,
        setIsSaving,
        config,
        setConfig,
      }}
    >
      <div className="col px-0">
        <div className="card border-0">
          <div
            className={classNames('card-body vstack gap-4', {
              container: config.isCenterEditor,
            })}
          >
            <Menu id={id} isEdit={isEdit} metadata={metadata} data={data} />

            <div
              className={classNames({
                'd-none': tab !== 0,
              })}
            >
              <ContentLink />

              <Editor
                id={id}
                initialData={initialData}
                metadata={metadata}
                name={form.name}
                setName={(name: string) => {
                  setForm({ ...form, name });
                }}
                setIsSaving={setIsSaving}
                extraData={{
                  postName: form.name,
                  postOverview: form.overview,
                }}
                type={type}
                ref={editorRef}
              />
            </div>

            <div
              className={classNames({
                'd-none': tab !== 1,
              })}
            >
              <Preview content={previewContent} metadata={metadata} />
            </div>

            <div
              className={classNames({
                'd-none': tab !== 4,
              })}
            >
              <Content
                id={id}
                isEdit={isEdit}
                metadata={metadata}
                sections={data.sections}
              />
            </div>
          </div>
        </div>
      </div>
    </EditPostContext.Provider>
  );
}
